package edu.cmu.javadb;

import java.io.*;
import java.net.URL;
import java.util.*;

import edu.cmu.javadb.exceptions.NotComparableException;

public class JavaDBClassLoader extends ClassLoader {
	private ClassLoader sysClassLoader;
	String[] externalPath = null;

	public JavaDBClassLoader() {
		super(JavaDBClassLoader.class.getClassLoader());
		sysClassLoader = getSystemClassLoader();
		this.externalPath = new String[] {};
	}

	public JavaDBClassLoader(String[] value) {
		super(JavaDBClassLoader.class.getClassLoader());
		sysClassLoader = getSystemClassLoader();
		this.externalPath = value;
	}

	public Class<?> loadClass(String className) throws ClassNotFoundException {
	//	System.out.println("class loading: " + className);
		Class<?> cl = null;
		try {
			cl = sysClassLoader.loadClass(className);
		} catch (ClassNotFoundException e) {
			try {
				cl = findClass(className);
			} catch (ClassNotFoundException e1) {
				e.printStackTrace();
			}
		}

		if (RecordInfo.getRecordInfo(cl) == null) {
			try {
				RecordInfo.RegisterRecordInfo(cl);

				if (RecordInfo.getRecordInfo(cl) != null) {
					System.out.println("class registered: " + cl.toString());
				} else {
				//	System.out
				//			.println("class not registered: " + cl.toString());
				}

			} catch (NotComparableException e) {
				e.printStackTrace();
			}
		}
		//System.out.println("class loaded: " + className);
		return cl;
	}

	public Class<?> findClass(String className) throws ClassNotFoundException {

		byte classByte[];
		Class<?> result = null;
		result = (Class<?>) classes.get(className);
		if (result != null) {
			return result;
		}

		try {
			return findSystemClass(className);
		} catch (Exception e) {
		}

		if (externalPath != null) {
			for (String path : externalPath) {
				String classPath = path + File.separatorChar
						+ className.replace('.', File.separatorChar) + ".class";
				// System.out.println("target classpath: " + classPath);
				try {
					classByte = loadClassData(classPath);
					result = defineClass(className, classByte, 0,
							classByte.length, null);
					classes.put(className, result);
				} catch (IOException e) {

				}
			}
		}

		if (result == null) {
			// System.out.println(className.replace('.', File.separatorChar) +
			// ".class");

			String classPath = ((String) ClassLoader.getSystemResource(
					className.replace('.', File.separatorChar) + ".class")
					.getFile()).substring(1);
			System.out.println("target classpath: " + classPath);
			try {
				classByte = loadClassData(classPath);
				result = defineClass(className, classByte, 0, classByte.length,
						null);
				classes.put(className, result);
			} catch (IOException e) {

			}
		}

		return result;
	}

	private byte[] loadClassData(String className) throws IOException {
		className = className.replace("%5c", "/");
		File f;
		f = new File(className);
		int size = (int) f.length();
		byte buff[] = new byte[size];
		FileInputStream fis = new FileInputStream(f);
		DataInputStream dis = new DataInputStream(fis);
		dis.readFully(buff);
		dis.close();
		return buff;
	}

	private Hashtable<String, Class<?>> classes = new Hashtable<String, Class<?>>();

}